// Fill out your copyright notice in the Description page of Project Settings.


#include "NeuronInterfaces.h"
#include "NeuronDataReader/NeuronDataReader.h"
#include "Math/UnrealMath.h"
#include "Misc/ScopeLock.h"


//////////////////////////////////////////////////////////////////////

class FNeuronCallbackBridge : public INeuronCallbackBridge
{
public:
	FNeuronCallbackBridge( )
	{
		static bool bInit = false;
		if (!bInit)
		{
			BRRegisterFrameDataCallback( nullptr, FrameCallback );
			bInit = !bInit;
		}
	}

	//~ Begin INeuronCallbackBridge
	virtual void RegisterDataSource( TSharedPtr<INeuronDataSourceAbility> DataSource ) override
	{
		FScopeLock Locker( &InternalLock );
		DataSources.Add( DataSource );
	}

	virtual void UnRegisterDataSource( TWeakPtr<INeuronDataSourceAbility> DataSource ) override
	{
		FScopeLock Locker( &InternalLock );
		if (!DataSource.Pin() && DataSources.Num() > 0)
		{
			for (int32 i = DataSources.Num() - 1; i >= 0; --i)
			{
				if (!DataSources[i].Pin())
				{
					DataSources.RemoveAt( i );
				}
			}
		}
	}

	virtual void UnRegisterDataSourceRef( TSharedPtr<INeuronDataSourceAbility>& DataSourceRef ) override
	{
		TWeakPtr<INeuronDataSourceAbility> WeakSource( DataSourceRef );
		DataSourceRef.Reset( );
		UnRegisterDataSource( WeakSource );
	}

	virtual TSharedPtr<INeuronDataSourceAbility> QueryDataSource( TSharedPtr<INeuronNetConfigBaseAbility> Config ) override
	{
		FScopeLock Locker( &InternalLock );

		if (Config)
		{
			for (auto WeakDataSource : DataSources)
			{
				auto DataSrc = WeakDataSource.Pin( );
				if (DataSrc && DataSrc->GetConfig( )->Equals( Config ))
				{
					return DataSrc;
				}
			}
		}

		return nullptr;
	}
	//~ End INeuronCallbackBridge

	static void FrameCallback( void* CustomObj, SOCKET_REF Socks, BvhDataHeader* Header, float* Data )
	{
		//FScopeLock Locker( &InternalLock );

		for (auto WeakDataSource : DataSources)
		{
			auto DataSource = WeakDataSource.Pin( );
			if (DataSource && (DataSource->GetConnectionSocket( ) == Socks))
			{
				DataSource->Dispatch( Header, Data );
				break;
			}
		}
	}

private:
	static FCriticalSection InternalLock;
	static TArray<TWeakPtr<INeuronDataSourceAbility>> DataSources;
};

FCriticalSection FNeuronCallbackBridge::InternalLock;
TArray<TWeakPtr<INeuronDataSourceAbility>> FNeuronCallbackBridge::DataSources;

INeuronCallbackBridge& INeuronCallbackBridge::Get()
{
	static FNeuronCallbackBridge Bridge;
	return Bridge;
}

//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////

